#include "Segmentor.h"
/**
* constructor
*/
Segmentor::Segmentor() : m_pWordDict(NULL), m_pCorrectDict(NULL)
{
}

Segmentor::~Segmentor()
{
}

void Segmentor::clear()
{
    m_VChar.clear();
    m_VWords.clear();
}
void Segmentor::setLine( const std::string &line)
{
    clear();
    StringTokenizer sToken;
    sToken.tokenize_by_char( line, m_VChar );
}
void Segmentor::setCorrectDict( dastrie::trie<std::string> * pDict)
{
    m_pCorrectDict = pDict;
}

void Segmentor::setWordDict( dastrie::trie<uint16_t> * pDict)
{
    m_pWordDict = pDict;
}
void Segmentor::setCorrectDict( std::string fileName)
{
}
void Segmentor::setWordDict( std::string fileName)
{
}

int Segmentor::changingWords( std::string phrase, int begin, int end)
{
    int i;
    std::vector<std::string> VTmp;

    Word word;
    std::vector< Word > VWords;

    //////////////////////////////////
    // tokenize
    StringTokenizer::tokenize( phrase, VTmp);

    // Token이 없을 때..
    if( VTmp.size() ==0) return begin;

    /////////////////////////////////////////
    // 검색된 phrase가 입력된 strig과 비교해서
    // 동일한 경우에만 처리
    std::string tmp = "";
    std::string target = "";
    for(i=0; i< (int) VTmp.size();i++)
    {
        tmp = tmp + VTmp[i];
    }
    for(i= m_VWords[begin].begin  ; i<=m_VWords[end].end ;i++)
    {
        target = target + m_VChar[i].getString(true);
    }

    if( tmp != target) return begin;
    /////////////////////////////////////////


    target = "";
    int idx =0;
    uint8_t len =0;
    for(i= m_VWords[begin].begin  ; i<=m_VWords[end].end ;i++)
    {
        if( target == "") 
        {
            word.setCharPtr(  m_VChar[i].getCharPtr() );
            word.begin = i;
            word.type = m_VChar[i].type;
        }

        target = target + m_VChar[i].getString(true);
        len = len +  m_VChar[i].getLength();

        if( target == VTmp[ idx ] )
        {
            word.setLength( len );
            word.end = i;
            len =0;
            target = "";
            idx++;

            VWords.push_back( word );
            word.clear();
        }
    }

    for( i = begin; i<=end; i++)
        m_VWords.erase( m_VWords.begin() +  begin );

    for( i= (int) VWords.size()-1; i>=0; i--)
        m_VWords.insert( m_VWords.begin() + begin, VWords[i] );

    return begin + VWords.size() -1;
}
void Segmentor::checkException( )
{
    int i,j, begin, end;
    std::string correct;
    dastrie::trie<std::string>::fsa_cursor state = m_pCorrectDict->fsa( "" );

    std::string value ="";
    for( i=0;i < (int) m_VWords.size();i++)
    {
        if( m_VWords[i].type == 0) continue;

        state =  m_pCorrectDict->fsa( "" );

        begin = -1; 
        end =-1;
        correct = "";

        for( j=i ; j < (int) m_VWords.size();j++)
        {
            if( m_VWords[j].getString() ==" ") continue;
            if( m_VWords[i].type == 0 && m_VWords[j].getString() !=" " ) break;

            if( state.query.length() != 0)
                state.append( " "+m_VWords[j].getString( true ) );
            else
                state.append( m_VWords[j].getString( true ) );

            state.next();

            if( state.isFinal() )  break;

            if(state.isValid()  == false) continue;

            begin = i;
            end = j;
            correct = state.value;
        }

        if( correct != "" && begin != -1 && end != -1)
        {
#ifdef _DEBUG_MODE_
std::cout <<"\n\n---------------------------------------\n";
std::cout << i <<"'th Correct:"<< correct <<"<-\tbegin:"<<begin <<"\tend:"<<end <<"\n";
std::cout <<"---------------------------------------\n";
#endif
            i = changingWords( correct, begin, end);
        }
    }

}
int Segmentor::getCharType( uint32_t code )
{
    // 숫자
    if( 0x30 <= code && code <= 0x39 ) return _NUM;

    if( 
            (   0x21 <= code && code <= 0x2f    )
        ||  (   0x3a <= code && code <= 0x40    )
        ||  (   0x5b <= code && code <= 0x60    )
        ||  (   0x7b <= code && code <= 0x7e    )
        ||  (   0xa1 <= code && code <= 0xcf    )

    ) return _SYM;             // 기호

    // 알파벳. a-z, A-Z.
    if( 
            (   0x41 <= code && code <= 0x5a    )
        ||  (   0x61 <= code && code <= 0x7a    )
    )
        return _ALP;

    // 한글 자모제외
    if(  0xac00 <= code && code <= 0xd7ff )
        return _KOR;


    if( 0xc0 <= code  )
        return _EXT;

    return _NON;
}
void Segmentor::analyze(  int begin_idx, int end_idx )
{
    dastrie::trie<uint16_t>::fsa_cursor state = m_pWordDict->fsa( "" );
    int i,j;

#ifdef _DEBUG_MODE_
std::cout <<"\n\n------------------------------------------------------\n";
std::cout << "Segmenting Index\n";
std::cout << "start(begin_idx):"<< begin_idx <<"\tto\t"<< "end(end_idx):"<<end_idx<<"\n";
std::cout <<"------------------------------------------------------\n";
#endif

    std::vector<   std::vector< uint16_t >     > VVWeight;
    std::vector< uint16_t > VTmp;
    std::vector< uint16_t > VMax;

    // index 값을 trie에서 읽어와서 2차원 배열인 VVWeight에 넣는다.
    for( i= end_idx  ; i>= begin_idx; i--)
    {
       // state.clear();
        state = m_pWordDict->fsa(  );
        VTmp.clear();

        for( j= i  ; j>= begin_idx; j--)
        {
            state.append( m_VChar[j].getString() );
            state.next();

            if( state.isFinal() )  break;

            if(state.isValid() )
            {
                VTmp.push_back( state.value );
            }
            else
                VTmp.push_back( 0 );
        }
        
        for( j=0;j< (int)VTmp.size();j++)
        {
            VTmp[j] = VTmp[j] +  _WEGIHT_TOKENS_  * j ;
        }
        
        if( VTmp.size() <=0) VTmp.push_back( 0 );

        VVWeight.push_back( VTmp );
    }

    // trim value
    // 13, 2, 0, 0 같이 값이 있을 경우 마지막 0,0의 값을 지워준다.
    for( i=0;i<(int) VVWeight.size();i++)
    {
        for( j=(int) VVWeight[i].size() -1;j>0; j--)
        {
            if( VVWeight[i][j] == 0)
                VVWeight[i].erase( VVWeight[i].begin() + j);
            else
                break;
        }
    }

    // 한 음절에서 시작하는 단어들의 최대치를 가져온다.
    for( i=0;i<(int) VVWeight.size();i++)
    {
        VMax.push_back( 0 );
        for( j=0; j< (int) VVWeight[i].size(); j++)
        {
            if( VMax[i] < VVWeight[i][j] )
                VMax[i] = VVWeight[i][j];
        }
    }

    int tmp_max;;
    int max;
    int max_idx;

    Word word;
    std::vector< Word > VStack;
    VStack.clear();
    for( i=0;i<(int) VVWeight.size();)
    {

#ifdef  _DEBUG_MODE_ 
std::cout <<"----------------------------\n";
std::cout <<"No:"<<i<<"->"<< m_VChar[ end_idx -i].getString() <<"\n";;
#endif

        max_idx =0;
        max = -1;
        if( VMax[i] != 0)
        for(j=0;j<(int)VVWeight[i].size();j++)
        {

#ifdef  _DEBUG_MODE_ 
std::cout << "    case "<<j <<"\t" << i + j +1 <<":" << VMax.size() <<":" ;
#endif

            tmp_max = -1;
            if( ( i + j +1 >= (int) VMax.size() ) || (  VMax[i + j +1] ==0  ) )
            {

#ifdef  _DEBUG_MODE_ 
std::cout << VVWeight[i][j] <<"+"<< _WEGIHT_MAX_TOKENS_  <<"="<<VVWeight[i][j] + _WEGIHT_MAX_TOKENS_ <<"\n";
#endif
                tmp_max = VVWeight[i][j] + _WEGIHT_MAX_TOKENS_;
            }
            else
            {

#ifdef  _DEBUG_MODE_ 
std::cout << VVWeight[i][j] <<"+"<< VMax[i + j +1]  <<"="<<VVWeight[i][j] + VMax[i + j +1] <<"\n";
#endif

                tmp_max = VVWeight[i][j] + VMax[i + j +1];
            }

            if( max < tmp_max)
            {
                max = tmp_max;
                max_idx = j;
            }
        }

        setValue( word, end_idx - (i+ max_idx) , end_idx - i);
        word.type = _KOR;
        VStack.push_back( word );


#ifdef  _DEBUG_MODE_ 
std::cout << "-->"<< word.getString() <<":\t";
std::cout << "max:"<<max <<"\t:"<< i <<"("<<  end_idx - (i+ max_idx)  <<")" <<"--"<< i+ max_idx <<"("<< end_idx - i <<")\n";
#endif


        i = i+ max_idx +1;
    }

    for(i=VStack.size()-1; i>=0;i--)
        m_VWords.push_back( VStack[i] );

#ifdef  _DEBUG_MODE_ 
std::cout <<"\n\n-----------------------------------------\n";
std::cout <<"단어 가중치 매트릭스\n";
std::cout <<"------------------------\n";
for( i=0;i<(int) VVWeight.size();i++)
{
    std::cout << i <<":"<<m_VChar[ end_idx -i  ].getString() <<":";
    std::cout << VMax[i] <<")\t";

    for(j=0;j<(int)VVWeight[i].size();j++)
    {
        std::cout <<"\t"<< m_VChar[ end_idx - j  -i ].getString()<<":";
        std::cout << VVWeight[i][j];
    }
    std::cout <<"\n";
}
std::cout <<"------------------------------\n";
#endif

}
void Segmentor::setValue( Word & word, int begin, int end)
{
    uint8_t length=0;
    word.clear();

    for( int i=begin ; i<=end; i++)
        length = length + m_VChar[ i ].getLength();

    word.setValue( m_VChar[ begin ].getCharPtr(), length);
    word.begin = begin;
    word.end = end;
    word.type = _KOR;
}

void Segmentor::segmenting( std::string &line, std::vector<Word>& VWords )
{
    clear();
    setLine( line );
    segmenting( VWords  );
}

void Segmentor::segmenting( std::vector<Word>& VWords  )
{
    int i,j;
    uint8_t length;
    int begin = 0;
    Word word;
    Character  token;


    for( i= 0; i< (int) m_VChar.size(); i++)
        m_VChar[i].type = getCharType( m_VChar[i].getUnicode() );

#ifdef  _DEBUG_MODE_ 
std::cout <<"\n\n-------------------------------------------------------\n";
std::cout <<"Tokenizer 결과\n";
std::cout <<"----------------\n";
std::cout <<"Token size:"<<m_VChar.size() <<"\n";
std::cout <<"---------------------------\n";
for( i= 0; i< (int) m_VChar.size(); i++)
{
    printf( "%d\t:%d\t:%s<-\t:%x<--\n",i,m_VChar[i].type, m_VChar[i].getString().c_str(),  m_VChar[i].getUnicode() );
}
std::cout <<"-------------------------------------------------------\n\n";
#endif
            

    token.clear();
    token.setValue(NULL,0);
    m_VChar.push_back( token );

    for( i= 0; i < (int)m_VChar.size()-1; i++)
    {
        if( m_VChar[i].type == m_VChar[i+1].type ) continue;
        //if( m_VChar[i].type != m_VChar[i+1].type )
        switch( m_VChar[i].type )
        {
            case _KOR:
                analyze( begin, i);
                break;

            case _ALP:
            case _NON:
            case _NUM:
            case _EXT:
                length =0;

                for(j = begin ; j <= i ; j++)
                    length = length  + m_VChar[j].getLength();

                word.clear();
                word.setValue ( m_VChar[ begin ].getCharPtr(), length );
                word.type = m_VChar[i].type;
                word.begin =  begin;
                word.end =  i;

                m_VWords.push_back( word );
                break;

            case _SYM:
                for(j = begin ; j <= i ; j++)
                {
                    word.clear();
                    word.type = _SYM;
                    word.begin = j;
                    word.end =j;

                    word.setValue (  m_VChar[ j ].getCharPtr(),m_VChar[ j ].getLength() );
                    m_VWords.push_back( word );
                }
                break;
            default:
                break;
        }
        begin = i+1;
    }


#ifdef _DEBUG_MODE_
std::cout <<"\n\nSegmenting 결과(예외 처리하기 전) \n";
std::cout <<"---------------\n";
for(i=0;i < (int)m_VWords.size();i++)
    std::cout << "Words "<<(int) i <<":" << m_VWords[i].getString() <<"/"<<(int) m_VWords[i].begin <<":"<< (int)m_VWords[i].end <<"\n";
#endif

    checkException( );

#ifdef _DEBUG_MODE_
std::cout <<"\n\nSegmenting 결과(예외 처리한 다음 ) \n";
std::cout <<"---------------\n";
for(i=0;i<(int)m_VWords.size();i++)
    std::cout << "Words "<<(int) i <<":" << m_VWords[i].getString() <<"/"<<(int) m_VWords[i].begin <<":"<< (int)m_VWords[i].end <<"\n";
#endif
    
    for(i=0;i<(int)m_VWords.size();i++)
        VWords.push_back(   m_VWords[i] );
}
